using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace ChEngine
{
    public class PgnFileOpeningBook:IOpeningBook
    {
        class Node
        {
            bool root;
            public Node(bool root)
            {
                move = new Movement(0, 0, MoveFlag.Nothing);
                this.root = root;
            }
            public Node()
                :this(false)
            {
                
            }
            private Movement move;
            public Movement Move
            {
                get { return move; }
                set { move = value; }
            }
            private ICollection<Node> nodes = new List<Node>();

            public ICollection<Node> Nodes
            {
                get { return nodes; }
                set { nodes = value; }
            }
            public void Append(IEnumerable<Movement> moves)
            {
                Queue<Movement> l = new Queue<Movement>(moves);
                if (l.Count > 0)
                {
                    Movement m = l.Dequeue();
                    bool appended = false;
                    foreach (Node n in nodes)
                    {
                        if (n.Move.BitDest == m.BitDest
                            && n.Move.BitStart == m.BitStart
                            && n.Move.Flag == m.Flag
                            )
                        {
                            n.Append(l);
                            appended = true;
                        }
                    }
                    if (!appended)
                    {
                        Node n = new Node();
                        n.Append(l);
                        nodes.Add(n);
                        n.Move = m;
                    }
                }
            }


            internal bool Probe(LinkedList<Movement> q, out Movement move)
            {
                move = null;
                List<Movement> candidates = new List<Movement>();
                if (q.Count == 0)
                {
                    if (nodes.Count == 0)
                        return false;
                    else
                    {
                        Random rnd = new Random();
                        foreach (Node n in nodes)
                            candidates.Add(n.Move);
                        move = candidates[rnd.Next(candidates.Count)];
                        return true;
                    }
                }
                Movement current = q.First.Value;
                q.RemoveFirst();
               
                if (!root)
                {
                    if (
                        Move.BitDest == current.BitDest
                        && Move.BitStart == current.BitStart
                        && Move.Flag == current.Flag
                        )
                    {
                        if (q.Count == 0)
                        {
                            foreach (Node reply in Nodes)
                                if (reply.Move.BitStart != reply.Move.BitDest)
                                    candidates.Add(reply.Move);
                        }
                        else
                        {
                            
                            Movement newMove;
                            foreach (Node reply in Nodes)
                            {
                                Movement first = q.First.Value;
                                if (reply.Probe(q, out newMove))
                                {
                                    if (newMove.BitStart != newMove.BitDest)
                                    {
                                        candidates.Add(newMove);
                                    }
                                }
                                q.AddFirst(first);
                            }
                            
                        }
                    }
                }
                else 
                {
                   
                    Movement newMove;
                    q.AddFirst(current);
                    foreach (Node reply in Nodes)
                    {
                        if (reply.Probe(q, out newMove))
                        {
                            if (newMove.BitStart != newMove.BitDest)
                                candidates.Add(newMove);
                        }
                        q.AddFirst(current);
                    }
                }



                if (candidates.Count == 0)
                {
                    //q.AddFirst(current);
                    return false;
                }
                else
                    if (candidates.Count == 1)
                    {
                        move = candidates[0];
                        return true;
                    }
                    else
                    {
                        Random rnd = new Random();
                        move = candidates[rnd.Next(candidates.Count)];
                        return true;
                    }

            }
        }
        Node root  = new Node(true);
        IMoveGeneratorOld moveGenerator;
        public PgnFileOpeningBook(TextReader reader,IMoveGeneratorOld moveGenerator)
        {
            this.moveGenerator = moveGenerator;
            string line;
            while( null != (line = reader.ReadLine()) )
            {
                line = line.Trim();
                if (!string.IsNullOrEmpty(line))
                {
                    try
                    {
                        PGNParser parser = new PGNParser(moveGenerator);

                        ICollection<Movement> movs = parser.ParseMoveText(line);
                        root.Append(movs);
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Book seems not well formatted.", e);
                    }
                }
            }
        }
        #region IOpeningBook Members

        public bool ProbeMove(ICollection<Movement> history, out Movement move)
        {
            /*
            List<Movement> test = new List<Movement>();
            BitBoard b = new BitBoard();
            test.Add(BitBoard.ParseNotation("e2e4", b, Side.White));
            test.Add(BitBoard.ParseNotation("d7d6", b, Side.White));
            test.Add(BitBoard.ParseNotation("d2d4", b, Side.White));
            
            LinkedList<Movement> q1 = new LinkedList<Movement>(test);
            root.Probe(q1, out move);
             */
            LinkedList<Movement> q = new LinkedList<Movement>(history);
            return root.Probe(q, out move);
        }

        #endregion
    }
}
